// /home/kev/Projects/mono/GtkTester/GtkTester/GtkTest/GtkTest/MessageOut.cs created with MonoDevelop
// User: kev at 15:36 10/08/2007
//
// To change standard headers go to Edit->Preferences->Coding->Standard Headers
//

using System;
using System.Text.RegularExpressions;
using Gtk;
using Glade;
using Pango;
using System.Configuration;
using System.Collections.Specialized;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;


namespace GtkTest
{
    public partial class MessageOut : Dialog
    {
        private bool done = true;             // Flag to stop
                                                // listener thread
        private bool listenerRunning = false;
        private UdpClient client;              // Client socket
        private IPAddress groupAddress;        // Multicast group
                                                // address
        private int localPort;                 // Local port to
                                                // receive messages
        private int remotePort;               // Remote port to send
                                                // messages
        private int ttl;
        private IPEndPoint remoteEP;
        private UnicodeEncoding encoding =
              new UnicodeEncoding();
        private string name;           // User name in chat
        private string message;        // Message to send
        private ThreadNotify notify;
        
        private FontDescription ReceivedHeadFont, SentHeadFont, StatusFont;
        private TextTag ReceivedHeadTag, SentHeadTag, SentTag, StatusTag;

        
        private static TargetEntry[] target_table = new TargetEntry []
        {
            new TargetEntry ("text/uri-list", 0, 0),
        };
        
        public MessageOut()
        {
            this.Build();
            Drag.DestSet(textMessage, DestDefaults.All, target_table, Gdk.DragAction.Copy);
            
            try
            {
                // Read the application configuration file
                NameValueCollection configuration = ConfigurationSettings.AppSettings;
                groupAddress    = IPAddress.Parse(configuration["GroupAddress"]);
                localPort       = int.Parse(configuration["LocalPort"]);
                remotePort      = int.Parse(configuration["RemotePort"]);
                ttl             = int.Parse(configuration["TTL"]);
            }            
            catch(Exception e)
            {
                using(MessageDialog se = new MessageDialog(null, DialogFlags.Modal, MessageType.Error, ButtonsType.Close,
                                                      "Error in application configuration file! : " + e.Message))
                {
                    se.Run();
                    se.Destroy();                        
                }
            }
            
            buttonStart.Sensitive   = true;
            buttonStop.Sensitive    = false;
            buttonSend.Sensitive    = false;
            
            // build font's for history window
            // Received header messages
            ReceivedHeadFont = FontDescription.FromString("Bold");
            ReceivedHeadTag = new TextTag("receivedHead");                
            ReceivedHeadTag.FontDesc = ReceivedHeadFont;
            ReceivedHeadTag.Foreground = "Red";
            
            // Sent header messages
            SentHeadFont = FontDescription.FromString("Bold");
            SentHeadTag = new TextTag("sentHead");                
            SentHeadTag.FontDesc = SentHeadFont;
            SentHeadTag.Foreground = "Blue";
            
            // Sent messages
            SentTag = new TextTag("sent");                
            SentTag.Foreground = "Dark Grey";
            
            // Status messages
            StatusFont = FontDescription.FromString("Italic");
            StatusTag = new TextTag("status");                
            StatusTag.FontDesc = StatusFont;
            StatusTag.Foreground = "Dark Green";
            
            
            textHistory.Buffer.TagTable.Add(ReceivedHeadTag);
            textHistory.Buffer.TagTable.Add(SentHeadTag);
            textHistory.Buffer.TagTable.Add(SentTag);
            textHistory.Buffer.TagTable.Add(StatusTag);
            
            Application.Run ();
        }

        protected virtual void DrapDrop (object o, DragDropArgs args)
        {
            Console.WriteLine("In DragDrop");
        }

        protected virtual void OnDragBegin (object o, DragBeginArgs args)
        {
            Console.WriteLine("In OnDragBegin");
        }

        protected virtual void OnDragDataRx (object o, DragDataReceivedArgs args)
        {
            bool success = false; 
            
            string data = System.Text.Encoding.UTF8.GetString ( 
            args.SelectionData.Data); 
            
            if(args.Info == 0) {    // uri-list 
                    string [  ] uri_list = Regex.Split (data, "\r\n"); 
                    foreach (string u in uri_list) { 
                        if (u.Length > 0) 
                        Console.WriteLine ("Got URI {0}", u); 
                    } 
                    success = true;                     
            } 
            Drag.Finish (args.Context, success, false, args.Time); 

        }

        protected virtual void OnWindowDeleteEvent (object o, Gtk.DeleteEventArgs args)
        {
            StopListener();
    		Application.Quit ();
            Console.WriteLine("Application has quit!");
    		args.RetVal = true;
    	}

        protected virtual void OnSendClick (object sender, System.EventArgs e)
        {            
            try
            {
                //send a message to the group
                TextBuffer buff = textMessage.Buffer;
                byte[] data = encoding.GetBytes(name + ": " + buff.Text);
                client.Send(data, data.Length, remoteEP);                
                
                //log last message to history buffer
                TextBuffer LogBuff = textHistory.Buffer;
                string time = DateTime.Now.ToLongTimeString();
                LogBuff = textHistory.Buffer;                
                
                TextIter end = LogBuff.GetIterAtMark(LogBuff.InsertMark);
                LogBuff.InsertWithTags(ref end, "(" + time + ") " + name + ": ", SentHeadTag);

                end = LogBuff.GetIterAtMark(LogBuff.InsertMark);
                LogBuff.InsertWithTags(ref end, buff.Text + "\r\n", SentTag);
                
                textHistory.ScrollMarkOnscreen(textHistory.Buffer.InsertMark);
                buff.Clear();
                textMessage.FocusChild = this;
            }
            catch(SocketException ex)
            {
                using(MessageDialog se = new MessageDialog(null, DialogFlags.DestroyWithParent | DialogFlags.Modal, MessageType.Error, 
                                                           ButtonsType.Close, ex.Message))
                {
                    se.Run();
                    se.Destroy();
                }
            }
        }
        
        protected virtual void OnStart (object sender, System.EventArgs e)
        {
            name = entryName.Text;
            entryName.IsEditable = false;
            try
            {
                // join the multicast gruop
                client = new UdpClient(localPort);
                Console.WriteLine(name + " has joined the chat");
                client.JoinMulticastGroup(groupAddress, ttl);                
                remoteEP = new IPEndPoint(groupAddress, remotePort);
                
                // Create the thread notify handler
                notify = new ThreadNotify(new ReadyEvent(DisplayRecievedMessage)); 
                // start the receiving thread
                Thread receiver = new Thread(new ThreadStart(Listener));
                receiver.IsBackground = true;   // will be stopped when main thread is stopped
                receiver.Start();
                // Send the first message to the group
                byte[] data = encoding.GetBytes(name + " has joined the chat");                
                client.Send(data, data.Length, remoteEP);
                
                buttonStart.Sensitive   = false;
                buttonStop.Sensitive    = true;
                buttonSend.Sensitive    = true;
                
                listenerRunning = true;
            }
            catch(SocketException ex)
            {
                using(MessageDialog se = new MessageDialog(null, DialogFlags.DestroyWithParent | DialogFlags.Modal, MessageType.Error, 
                                                           ButtonsType.Close, ex.Message))
                {
                    se.Run();
                    se.Destroy();
                }
            }
        }
        
        private void Listener()
        {
            done = false;
            Console.WriteLine("Thread Listener Started");
            try
            {
                while(!done)
                {
                    IPEndPoint ep = null;
                    byte[] buffer = client.Receive(ref ep);
                    message = encoding.GetString(buffer);
                    Console.WriteLine("Received data of len " + message.Length);
                    notify.WakeupMain();    // Wake-up main thread and invoke delegate
                }
            }
            catch(SocketException ex)
            {
                if (ex.ErrorCode == 10004)
                {
                    // Ignore cancel blocking call error messages
                }
                else
                {
                    using(MessageDialog se = new MessageDialog(null, DialogFlags.DestroyWithParent | DialogFlags.Modal, MessageType.Error, 
                                                           ButtonsType.Close, ex.Message))
                    {
                        se.Run();
                        se.Destroy();
                    }
                }
            }   
        }
        
        private void DisplayRecievedMessage()
        {
            TextBuffer buff;
            TextIter end;
            string time = DateTime.Now.ToLongTimeString();
            buff = textHistory.Buffer;
            end = buff.GetIterAtMark(buff.InsertMark);

            string[] splitmessage = message.Split(new char[] { ':' }, 2);
            if(splitmessage.GetUpperBound(0) > 0)
            {
                buff.InsertWithTags(ref end, "(" + time + ") " + splitmessage[0] + ":", ReceivedHeadTag);
                end = buff.GetIterAtMark(buff.InsertMark);
                buff.Insert(ref end, splitmessage[1] + "\r\n");
            }
            else
            {
                buff.Insert(ref end, "(" + time + ") ");
                end = buff.GetIterAtMark(buff.InsertMark);
                buff.InsertWithTags(ref end, message + "\r\n", StatusTag);
            }
            textHistory.ScrollMarkOnscreen(textHistory.Buffer.InsertMark);
        }

        private void StopListener ()
        {
            if(listenerRunning)
            {
                // Send a leaving message to the group
                byte[] data = encoding.GetBytes(name + " has left the chat");
                client.Send(data, data.Length, remoteEP);
                // Leave the group
                client.DropMulticastGroup(groupAddress);
                client.Close();
                // Tell the receiving thread to stop
                done = true;
                buttonStart.Sensitive   = true;
                buttonStop.Sensitive    = false;
                buttonSend.Sensitive    = false;
                
                listenerRunning = false;
            }
        }

        protected virtual void OnButtonStopClicked (object sender, System.EventArgs e)
        {
            Console.WriteLine("Leaving...");
            StopListener();
        }
    }
}
